/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   Module Name          : com.yaodian100.ec.taobao.service.impl.TaobaoServiceImpl
   Module Description   :

   Date Created      : 2010/12/14
   Original Author   : jeffma
   Team              : yaodian100
   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   MODIFICATION HISTORY
   ------------------------------------------------------------------------------
   Date Modified       Modified by       Comments
   ------------------------------------------------------------------------------
   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
package com.yaodian100.ec.taobao.service.impl;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import com.taobao.api.ApiException;
import com.taobao.api.DefaultTaobaoClient;
import com.taobao.api.FileItem;
import com.taobao.api.TaobaoClient;
import com.taobao.api.TaobaoRequest;
import com.taobao.api.TaobaoResponse;
import com.taobao.api.domain.Item;
import com.taobao.api.domain.Order;
import com.taobao.api.domain.SellerCat;
import com.taobao.api.domain.Trade;
import com.taobao.api.domain.User;
import com.taobao.api.request.ItemAddRequest;
import com.taobao.api.request.ItemDeleteRequest;
import com.taobao.api.request.ItemGetRequest;
import com.taobao.api.request.ItemUpdateRequest;
import com.taobao.api.request.ItemsCustomGetRequest;
import com.taobao.api.request.ItemsOnsaleGetRequest;
import com.taobao.api.request.SellercatsListGetRequest;
import com.taobao.api.request.TimeGetRequest;
import com.taobao.api.request.TradeGetRequest;
import com.taobao.api.request.TradeMemoAddRequest;
import com.taobao.api.request.TradesSoldGetRequest;
import com.taobao.api.request.TradesSoldIncrementGetRequest;
import com.taobao.api.request.UserGetRequest;
import com.taobao.api.response.ItemAddResponse;
import com.taobao.api.response.ItemDeleteResponse;
import com.taobao.api.response.ItemGetResponse;
import com.taobao.api.response.ItemUpdateResponse;
import com.taobao.api.response.ItemsCustomGetResponse;
import com.taobao.api.response.ItemsOnsaleGetResponse;
import com.taobao.api.response.SellercatsListGetResponse;
import com.taobao.api.response.TimeGetResponse;
import com.taobao.api.response.TradeGetResponse;
import com.taobao.api.response.TradesSoldGetResponse;
import com.taobao.api.response.TradesSoldIncrementGetResponse;
import com.taobao.api.response.UserGetResponse;
import com.yaodian100.core.cache.exception.CacheException;
import com.yaodian100.core.cache.service.CacheService;
import com.yaodian100.core.common.dao.impl.CommonCriteria;
import com.yaodian100.core.common.utils.BytesUtil;
import com.yaodian100.core.common.utils.DateUtil;
import com.yaodian100.core.common.utils.ServerValue;
import com.yaodian100.core.common.utils.StringUtils;
import com.yaodian100.core.menu.entity.Menu;
import com.yaodian100.core.menu.entity.Option;
import com.yaodian100.core.menu.service.MenuService;
import com.yaodian100.core.template.service.TemplateService;
import com.yaodian100.ec.category.entity.Category;
import com.yaodian100.ec.category.service.CategoryService;
import com.yaodian100.ec.member.entity.Balance;
import com.yaodian100.ec.member.entity.Member;
import com.yaodian100.ec.member.service.MemberService;
import com.yaodian100.ec.order.entity.Orders;
import com.yaodian100.ec.order.service.OrderService;
import com.yaodian100.ec.product.entity.Brand;
import com.yaodian100.ec.product.entity.Product;
import com.yaodian100.ec.product.entity.SpecificationInventory;
import com.yaodian100.ec.product.service.ProductService;
import com.yaodian100.ec.shelf.entity.Shelf;
import com.yaodian100.ec.shelf.service.ShelfService;
import com.yaodian100.ec.shopping.entity.CartOrders;
import com.yaodian100.ec.shopping.service.CartOrdersService;
import com.yaodian100.ec.taobao.service.TaobaoService;

/**
 * @author jeffma
 * 
 */
public class TaobaoServiceImpl implements TaobaoService {

	private final Logger logger = LoggerFactory.getLogger(TaobaoServiceImpl.class);

	private String[] apiKey = { "12126931", "12156713", "12209966" };
	private String[] apiSecret = { "139df2dcd253758b913704ed93c2153d", "735280cea4f5efad9f0c382813d48af5",
			"13dedb9d88ae760a7b00a6bfd93242a3" };
	private String apiUrl = "http://gw.api.taobao.com/router/rest";
	private Menu categoryMenu;
	private Long defaultCid = 50019379L;
	private String defaultNick = "耀点100官网";

	private CacheService<Category> cacheCategory;
	private CacheService<Brand> cacheBrand;

	@Autowired
	private ProductService productService;
	@Autowired
	private ShelfService shelfService;
	@Autowired
	private CategoryService categoryService;
	@Autowired
	private CartOrdersService cartService;
	@Autowired
	private OrderService orderService;
	@Autowired
	private MenuService menuService;
	@Autowired
	private MemberService memberService;
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private TemplateService templateService;

	public Menu getCategoryMenu() {
		if (categoryMenu == null) {
			categoryMenu = menuService.get("Taobao.category");
		}
		return categoryMenu;
	}

	/**
	 * @param cacheCategory the cacheCategory to set
	 */
	public void setCacheCategory(CacheService<Category> cacheCategory) {
		this.cacheCategory = cacheCategory;
	}

	/**
	 * @param cacheBrand the cacheBrand to set
	 */
	public void setCacheBrand(CacheService<Brand> cacheBrand) {
		this.cacheBrand = cacheBrand;
	}

	/**
	 * @param apiKey the apiKey to set
	 */
	public void setApiKey(String[] apiKey) {
		this.apiKey = apiKey;
	}

	/**
	 * @param apiSecret the apiSecret to set
	 */
	public void setApiSecret(String[] apiSecret) {
		this.apiSecret = apiSecret;
	}

	/**
	 * @param apiUrl the apiUrl to set
	 */
	public void setApiUrl(String apiUrl) {
		this.apiUrl = apiUrl;
	}

	public String md5Signature(Map params, String apiSecretStr) {
		String result = null;
		StringBuffer orgin = getBeforeSign(params, new StringBuffer(apiSecretStr));
		if (orgin == null) {
			return result;
		}
		// secret last
		orgin.append(apiSecretStr);
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			result = BytesUtil.bytesToHex(md.digest(orgin.toString().getBytes("utf-8")));
		} catch (Exception e) {
			throw new java.lang.RuntimeException("sign error !");
		}
		return result;
	}

	private StringBuffer getBeforeSign(Map params, StringBuffer orgin) {
		if (params == null) {
			return null;
		}
		Map<String, String> treeMap = new TreeMap<String, String>();
		treeMap.putAll(params);
		Iterator<String> iter = treeMap.keySet().iterator();
		while (iter.hasNext()) {
			String name = iter.next();
			orgin.append(name).append(params.get(name));
		}
		return orgin;
	}

	private TaobaoResponse executeHttp(TaobaoRequest req, String sessionKey) {
		TaobaoClient client = new DefaultTaobaoClient(apiUrl, apiKey[0], apiSecret[0]);
		TaobaoResponse res = null;
		try {
			if (StringUtils.isBlank(sessionKey)) {
				res = client.execute(req);
			} else {
				res = client.execute(req, sessionKey);
			}
			logger.debug("input:{}, output:{}", res.getParams(), res.getBody());
			if (!res.isSuccess()) {
				logger.error("input:{}, output:{}", res.getParams(), res.getBody());
			}
		} catch (ApiException e) {
			e.printStackTrace();
		}
		return res;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemAdd(com.yaodian100.ec.product.entity.Product,
	 * java.lang.String, com.yaodian100.ec.shelf.entity.Shelf)
	 */
	public String itemAdd(Product product, long qty, Shelf shelf) {
		ItemAddRequest req = new ItemAddRequest();

		req.setNum(qty); // 商品数量
		req.setPrice(shelf.getShelfPrice().toString()); // 商品价格
		req.setType("fixed");
		req.setStuffStatus("new");
		req.setTitle(StringUtils.abbreviate(product.getProductName(), 57)); // 宝贝标题
		// req.setIsEx(Boolean.TRUE);
		// req.setIsTaobao(Boolean.TRUE);

		String desc = productService.readFromFile(product.getProductId(), "description");
		if (StringUtils.isBlank(desc)) {
			desc = productService.readFromFile(product.getProductId(), "details");
			if (StringUtils.isBlank(desc)) {
				desc = productService.readFromFile(product.getProductId(), "brief");
			}
		}
		if (StringUtils.isBlank(desc)) {
			desc = product.getProductName();
		}
		if (desc.length() < 5) {
			desc += "＊＊＊＊＊＊";
		}

		req.setDesc(StringUtils.abbreviate(desc, 24995)); // 宝贝描述, 字数要大于5个字符，小于25000个字符
		req.setLocationState("上海"); //
		req.setLocationCity("上海"); //

		Category category = categoryService.get(Long.valueOf(product.getMainCategoryID()));
		String[] categoryPath = StringUtils.split(category.getCategoryPath(), "/");
		Option o = getCategoryMenu().getOptions().get(categoryPath[0]);
		req.setSellerCids(o.getMemo1());
		req.setCid(defaultCid);

		req.setHasInvoice(Boolean.TRUE);
		req.setHasWarranty(Boolean.TRUE);
		File image = new File("/yaodian100.com/web/PImg/" + product.getProductId().substring(0, 4) + "/"
				+ product.getProductId() + "/" + product.getProductId() + "_m500.jpg");
		if (image.exists() && image.isFile()) {
			req.setImage(new FileItem(image));// byte[] 商品主图片。类型:JPG,GIF;最大长度:500K
		}
		req.setPostageId(4790093L); // 4790093: 免運費
		req.setOuterId(product.getProductId());// 商品所属的产品ID

		Map<String, SpecificationInventory> siMap = productService.getSpecificationInventoryMap(product.getProductId());
		if (siMap == null || siMap.size() == 0 || siMap.size() > 1) {
			// 如果多個 spec or 無 spec 下架
			req.setApproveStatus("instock");
		} else {
			// 庫存小於 10 下架
			String key = siMap.keySet().iterator().next();
			SpecificationInventory si = siMap.get(key);
			if (si.getInventory().longValue() < 10) {
				req.setApproveStatus("instock");
			}
		}
		if (product.getProductCost().intValue() == 0) {
			// 無商品成本, 下架
			req.setApproveStatus("instock");
		} else if (shelf.getShelfPrice().subtract(product.getProductCost()).divide(shelf.getShelfPrice()).floatValue() <= 0.2) {
			// 商品成本小於 20%, 下架
			req.setApproveStatus("instock");
		}

		ItemAddResponse res = (ItemAddResponse) executeHttp(req, null);
		if (res != null && res.isSuccess()) {
			return res.getItem().getNumIid().toString();
		} else {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemGet(java.lang.Long)
	 */
	public Item itemGet(Long numIid) {
		ItemGetRequest req = new ItemGetRequest();
		req.setFields("detail_url,num_iid,title,type,cid,seller_cids,props,input_pids,input_str,num,valid_thru,list_time,delist_time,stuff_status,location,price,post_fee,express_fee,ems_fee,has_discount,has_invoice,has_warranty,has_showcase,increment,approve_status,postage_id,product_id,auction_point,property_alias,skus,outer_id,is_virtual,is_taobao,is_ex,is_timing,is_3D,score,volume,one_station,props_name,");
		req.setNumIid(numIid);
		Item result = null;
		ItemGetResponse res = (ItemGetResponse) executeHttp(req, null);
		if (res != null && res.isSuccess()) {
			result = res.getItem();
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemUpdate(com.yaodian100.ec.product.entity.Product,
	 * java.lang.Long, com.yaodian100.ec.shelf.entity.Shelf)
	 */
	public boolean itemUpdate(Product product, long qty, Shelf shelf) {
		if (product != null && shelf != null) {
			String numIid = itemsCustomGet(product.getProductId());
			if (StringUtils.isNotBlank(numIid) && NumberUtils.isNumber(numIid)) {
				ItemUpdateRequest req = new ItemUpdateRequest();
				req.setNumIid(Long.valueOf(numIid));
				req.setNum(qty); // 商品数量
				req.setPrice(shelf.getShelfPrice().toString()); // 商品价格
				req.setTitle(StringUtils.abbreviate(product.getProductName(), 57)); // 宝贝标题
				// req.setIsEx(Boolean.TRUE);
				// req.setIsTaobao(Boolean.TRUE);

				String desc = productService.readFromFile(product.getProductId(), "description");
				if (StringUtils.isBlank(desc)) {
					desc = productService.readFromFile(product.getProductId(), "details");
					if (StringUtils.isBlank(desc)) {
						desc = productService.readFromFile(product.getProductId(), "brief");
					}
				}
				if (StringUtils.isBlank(desc)) {
					desc = product.getProductName();
				}
				if (desc.length() < 5) {
					desc += "＊＊＊＊＊＊";
				}

				req.setDesc(StringUtils.abbreviate(desc, 24995)); // 宝贝描述, 字数要大于5个字符，小于25000个字符

				Map<String, SpecificationInventory> siMap = productService.getSpecificationInventoryMap(product.getProductId());
				if (siMap == null || siMap.size() == 0 || siMap.size() > 1) {
					// 如果多個 spec or 無 spec 下架
					req.setApproveStatus("instock");
				} else {
					// 庫存小於 10 下架
					String key = siMap.keySet().iterator().next();
					SpecificationInventory si = siMap.get(key);
					if (si.getInventory().longValue() < 10) {
						req.setApproveStatus("instock");
					}
				}
				if (product.getProductCost().intValue() == 0) {
					// 無商品成本, 下架
					req.setApproveStatus("instock");
				} else if (shelf.getShelfPrice().subtract(product.getProductCost()).divide(shelf.getShelfPrice()).floatValue() <= 0.2) {
					// 商品成本小於 20%, 下架
					req.setApproveStatus("instock");
				}

				ItemUpdateResponse res = (ItemUpdateResponse) executeHttp(req, null);
				if (res != null && res.isSuccess()) {
					return true;
				}
			} else {
				logger.warn("taobao item not found, product: " + product.getProductId());
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemDelete(java.lang.String)
	 */
	public boolean itemDelete(String productId) {
		ItemDeleteRequest req = new ItemDeleteRequest();
		String numIid = itemsCustomGet(productId);

		boolean result = false;
		if (StringUtils.isNotBlank(numIid) && NumberUtils.isNumber(numIid)) {
			req.setNumIid(Long.valueOf(numIid));
			ItemDeleteResponse res = (ItemDeleteResponse) executeHttp(req, null);
			if (res != null && res.isSuccess()) {
				result = true;
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemsCustomGet(java.lang.String)
	 */
	public String itemsCustomGet(String productId) {
		ItemsCustomGetRequest req = new ItemsCustomGetRequest();
		req.setOuterId(productId);
		req.setFields("iid,sku");
		ItemsCustomGetResponse res = (ItemsCustomGetResponse) executeHttp(req, null);

		String iid = null;
		if (res != null && res.isSuccess() && res.getItems() != null) {
			if (res.getItems().size() == 1) {
				iid = res.getItems().get(0).getNumIid().toString();
			} else if (res.getItems().size() > 1) {
				iid = res.getItems().get(0).getNumIid().toString();
				logger.warn("productId exist more than one iid, iids:" + res.getBody());
			} else {
				// not found, by pass, return null
			}
		}
		return iid;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#itemsGet(int)
	 */
	@Override
	public Map<String, String> itemsGet(long page) {
		ItemsOnsaleGetRequest req = new ItemsOnsaleGetRequest();
		req.setFields("num_iid,title,outer_id,is_taobao,is_ex");
		req.setCid(defaultCid);

		Map<String, String> result = new HashMap();
		if (page > 0) {
			req.setPageNo(page);
			ItemsOnsaleGetResponse res = (ItemsOnsaleGetResponse) executeHttp(req, null);

			if (res != null && res.isSuccess()) {
				result.put("totalSize", res.getTotalResults().toString());
				for (Item item : res.getItems()) {
					result.put(item.getOuterId(), item.getNumIid().toString());
				}
			}
		} else {
			result.putAll(itemsGet(1));
			int count = Integer.parseInt(result.get("totalSize")) / 40;
			if (Integer.parseInt(result.get("totalSize")) % 40 != 0) {
				count++;
			}
			for (int i = 2; i <= count; i++) {
				result.putAll(itemsGet(i));
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#timeGet()
	 */
	public Date timeGet() {
		TimeGetRequest req = new TimeGetRequest();
		TimeGetResponse res = (TimeGetResponse) executeHttp(req, null);

		Date result = null;
		if (res != null && res.isSuccess()) {
			result = res.getTime();
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#usersGet()
	 */
	public Member usersGet(String logidId) {
		Member result = null;

		CommonCriteria criteria = new CommonCriteria();
		criteria.addEq("loginId", logidId);
		criteria.addEq("applySource", "taobaomember");
		List<Member> members = memberService.getList(0, 1, criteria, null);
		if (members != null && members.size() > 0) {
			result = members.get(0);
		} else {
			UserGetRequest req = new UserGetRequest();
			req.setFields("nick,email,user_id");
			if (StringUtils.indexOf(logidId, "@top") > 0) {
				req.setNick(StringUtils.removeEnd(logidId, "@top"));
			} else {
				req.setNick(logidId);
			}

			UserGetResponse res = (UserGetResponse) executeHttp(req, null);
			if (res != null && res.isSuccess() && res.getUser() != null) {

				if (members != null && members.size() > 0) {
					result = members.get(0);
				} else {
					User taobaoUser = res.getUser();
					result = new Member();
					result.setLoginId(logidId);
					if (StringUtils.isNotBlank(taobaoUser.getEmail())) {
						result.setEmail(taobaoUser.getEmail());
					} else {
						result.setEmail("xx");
					}
					if (taobaoUser.getUserId() != null) {
						result.setName(StringUtils.abbreviate(taobaoUser.getUserId().toString(), 16));
					} else {
						result.setName(StringUtils.removeEnd(logidId, "@top"));
					}
					result.setRank("1");
					result.setStatus("00");
					result.setActiveDate(new Date());
					result.setPwd("xx");
					result.setClientIp("");
					result.setApplySource("taobaomember");
					result.setDomainSrc(ServerValue.getFrontHostname().substring(7)); // remove http:// length 7
					result = memberService.save(result);
					Balance balance = memberService.newBalance(result);
					result.setBalance(balance);
				}
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#isValid(java.lang.String)
	 */
	public boolean isValid(String queryString, String appKey) {
		boolean result = false;
		String sign = "xx";
		Date timestamp = null;
		Map data = new HashMap();
		if (StringUtils.isNotBlank(queryString)) {
			String[] a1 = StringUtils.split(queryString, "&");
			for (String s : a1) {
				try {
					s = URLDecoder.decode(s, "utf-8");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				String[] a2 = StringUtils.split(s, "=");
				if (a2.length == 2) {
					if ("sign".equals(a2[0])) {
						sign = a2[1];
					} else if ("timestamp".equals(a2[0])) {
						timestamp = DateUtil.convertStringToDate("yyyy-MM-dd HH:mm:ss", a2[1]);
						data.put(a2[0], a2[1]);
					} else {
						data.put(a2[0], a2[1]);
					}
				}
			}
		}

		int keySeq = 0;
		for (int i = 0; i < apiKey.length; i++) {
			if (appKey.equals(apiKey[i])) {
				keySeq = i;
			}
		}
		if (keySeq < 0) {
			keySeq = 0;
		}

		// sign 正確
		String localSign = md5Signature(data, apiSecret[keySeq]);
		logger.debug("sign:{}/{}, localSign:{}, date:{}, queryString:{}", new Object[] { sign, appKey, localSign, data,
				queryString });
		if (sign.equals(localSign)) {
			// 時間在 20分鐘內
			if (timestamp != null) {
				Date now = DateUtil.getCurrentTime();
				long t = (timestamp.getTime() - now.getTime()) / 60000;
				if (20 > t && t > -20) {
					result = true;
				} else {
					logger.error("sign ok, time fail, current:{}, taobao time: {}, queryString:{}", new Object[] { now,
							timestamp, queryString });
				}
			} else {
				result = true;
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#isValid(java.lang.String)
	 */
	@Override
	public boolean isValid(String queryString) {
		boolean result = false;
		String sign = "xx";
		Date timestamp = null;
		Map data = new HashMap();
		if (StringUtils.isNotBlank(queryString)) {
			String[] a1 = StringUtils.split(queryString, "&");
			for (String s : a1) {
				String[] a2 = StringUtils.split(s, "=");
				if (a2.length == 2) {
					if ("sign".equals(a2[0])) {
						sign = a2[1];
					} else if ("timestamp".equals(a2[0])) {
						timestamp = DateUtil.convertStringToDate("yyyy-MM-dd HH:mm:ss", a2[1]);
						data.put(a2[0], a2[1]);
					} else {
						data.put(a2[0], a2[1]);
					}
				}
			}
		}

		int keySeq = 0;
		if (data.get("api_key") != null && StringUtils.isNotBlank(data.get("api_key").toString())) {
			for (int i = 0; i < apiKey.length; i++) {
				if (apiKey[i].equals(data.get("api_key"))) {
					keySeq = i;
				}
			}
		}

		// sign 正確
		String localSign = md5Signature(data, apiSecret[keySeq]);
		logger.debug("sign:{}, localSign:{}, date:{}, queryString:{}", new Object[] { sign, localSign, data, queryString });
		if (sign.equals(localSign)) {
			// // 時間在 20分鐘內
			if (timestamp != null) {
				Date now = DateUtil.getCurrentTime();
				long t = (timestamp.getTime() - now.getTime()) / 60000;
				if (20 > t && t > -20) {
					result = true;
				} else {
					logger.error("sign ok, time fail, current:{}, taobao time: {}, queryString:{}", new Object[] { now,
							timestamp, queryString });
				}
			} else {
				result = true;
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#tradesSoldIncrementGet(java.util.Date, java.util.Date,
	 * java.lang.String)
	 */
	public long tradesSoldIncrementGet(Date start, Date end, String status) {
		TradesSoldIncrementGetRequest req = new TradesSoldIncrementGetRequest();
		req.setFields("seller_nick,buyer_nick,title,type,created,tid,seller_rate,buyer_rate,status,payment,discount_fee,adjust_fee,post_fee,total_fee,pay_time,end_time,modified,consign_time,buyer_obtain_point_fee,point_fee,real_point_fee,received_payment,commission_fee,pic_path,num_iid,num,price,cod_fee,cod_status,shipping_type,receiver_name,receiver_state,receiver_city,receiver_district,receiver_address,receiver_zip,receiver_mobile,receiver_phone");

		Date now = DateUtil.getCurrentTime();
		int dateNum = Integer.valueOf(DateUtil.convertDateToString("HHmm", now));
		if (start == null) {
			if (dateNum < 5) {
				Calendar c = DateUtil.getTargetDateAddDays(-1, DateUtil.getFirstTimestamp(Calendar.DAY_OF_MONTH, now));
				req.setStartModified(c.getTime());
			} else {
				req.setStartModified(DateUtil.getFirstTimestamp(Calendar.DAY_OF_MONTH, now));
			}

		} else {
			req.setStartModified(start);
		}
		if (end == null) {
			if (dateNum < 5) {
				Calendar c = DateUtil.getTargetDateAddDays(-1, DateUtil.getLastTimestamp(Calendar.DAY_OF_MONTH, now));
				req.setStartModified(c.getTime());
			} else {
				req.setEndModified(now);
			}
		} else {
			req.setEndModified(end);
		}
		if (StringUtils.isBlank(status)) {
			req.setStatus("TRADE_FINISHED");
		} else {
			req.setStatus(status);
		}
		req.setUseHasNext(Boolean.TRUE);

		long count = 0;
		TradesSoldIncrementGetResponse res = null;
		do {
			res = (TradesSoldIncrementGetResponse) executeHttp(req, null);
			if (res != null && res.isSuccess() && res.getTrades() != null) {
				for (Trade trade : res.getTrades()) {
					Orders order = tradeToOrder(trade);
					if (order != null) {
						count++;
					}
				}
			}
		} while (res != null && res.getHasNext());
		return count;
	}

	private Orders tradeToOrder(Trade trade) {
		Orders result = null;

		try {
			if (StringUtils.isBlank(trade.getSellerMemo())) {
				Member member = usersGet(trade.getBuyerNick());
				CartOrders cart = cartService.getCart(member.getMemberId(), null);
				if (cart == null) {
					// create main cart
					cartService.createCart(member.getMemberId(), null, null, null, 0, null, null);
				}
				cart = cartService.createCart(member.getMemberId(), null, null, null, 0, null, null);
				for (Order order : trade.getOrders()) {
					// create temp cart
					cart = cartService.addProduct(member.getMemberId(), cart.getCartId(), order.getOuterIid(), null, null, order
							.getNum().intValue(), null, null);
				}
				if ("xx".equals(member.getEmail())) {
					member.setEmail(trade.getBuyerEmail());
					memberService.save(member);
				}
				cart.setReceiverEmail(trade.getBuyerEmail());
				cart.setReceiverExt(" ");
				cart.setReceiverMobile(trade.getReceiverMobile());
				cart.setReceiverName(trade.getReceiverName());
				cart.setReceiverPhone(trade.getReceiverPhone());
				cart.setReceiverPhoneArea(" ");
				cart.setReceiverZip(trade.getReceiverZip());
				cart.setReceiverProvince(trade.getReceiverState());
				cart.setReceiverCity(trade.getReceiverCity());
				cart.setReceiverArea(trade.getReceiverDistrict());
				cart.setReceiverAddr(trade.getReceiverAddress());
				cart.setMemo("msg:" + trade.getBuyerMessage() + ",memo:" + trade.getBuyerMemo());

				Map addonInfo = new HashMap();
				addonInfo.put("remoteAddr", ""); // ?! unknow
				addonInfo.put("applySource", "taobao");
				addonInfo.put("userAgent", "");
				addonInfo.put("coupon", ""); // couponItem uuid
				addonInfo.put("couponProduct", "");

				Map payment = new HashMap();
				payment.put("paymentType", "??"); // ?! unknow
				payment.put("vrtBlnc", "0");
				payment.put("bonus", "0");
				result = orderService.createOrderWithPay(cart, addonInfo, payment);

				TradeMemoAddRequest req = new TradeMemoAddRequest();
				req.setTid(trade.getTid());
				req.setMemo(result.getOrderId());
				TaobaoResponse res = executeHttp(req, null);
				if (res == null || !res.isSuccess()) {
					logger.error("add trade memo fail, tid:" + trade.getTid() + ", order: " + result.getOrderId());
				}
			} else {
				logger.error("trade momo is not null, tid:" + trade.getTid() + ", memo: " + trade.getSellerMemo());
				result = orderService.get(trade.getSellerMemo());
			}

		} catch (Exception e) {
			logger.error("trade to order fail, tid:" + trade.getTid(), e);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#getTrades(java.util.Date, java.util.Date, java.lang.String,
	 * int)
	 */
	@Override
	public List<Orders> getTrades(Date start, Date end, String status, int page) {
		TradesSoldGetRequest req = new TradesSoldGetRequest();
		req.setFields("seller_nick,buyer_nick,title,type,created,tid,seller_rate,buyer_rate,status,payment,discount_fee,adjust_fee,post_fee,total_fee,pay_time,end_time,modified,consign_time,buyer_obtain_point_fee,point_fee,real_point_fee,received_payment,commission_fee,pic_path,num_iid,num,price,cod_fee,cod_status,shipping_type,receiver_name,receiver_state,receiver_city,receiver_district,receiver_address,receiver_zip,receiver_mobile,receiver_phone");
		if (start == null) {
			req.setStartCreated(DateUtil.getFirstTimestamp(Calendar.DAY_OF_MONTH, new Date()));
		} else {
			req.setStartCreated(start);
		}
		if (end == null) {
			req.setEndCreated(DateUtil.getLastTimestamp(Calendar.DAY_OF_MONTH, new Date()));
		} else {
			req.setEndCreated(end);
		}
		if (StringUtils.isBlank(status)) {
			req.setStatus("TRADE_FINISHED");
		} else {
			req.setStatus(status);
		}
		if (page > 0) {
			req.setPageNo(Long.valueOf(page));
		}

		List<Orders> result = new ArrayList();
		TradesSoldGetResponse res = (TradesSoldGetResponse) executeHttp(req, null);
		if (res != null && res.isSuccess()) {
			for (Trade trade : res.getTrades()) {
				Orders order = tradeToOrder(trade);
				if (order != null) {
					result.add(order);
				}
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#getTrade(java.lang.String)
	 */
	@Override
	public Orders getTrade(String tid) {
		TradeGetRequest req = new TradeGetRequest();
		req.setTid(Long.valueOf(tid));
		req.setFields("seller_nick,buyer_nick,title,type,created,tid,seller_rate,buyer_rate,status,payment,discount_fee,adjust_fee,post_fee,total_fee,pay_time,end_time,modified,consign_time,buyer_obtain_point_fee,point_fee,real_point_fee,received_payment,commission_fee,buyer_memo,seller_memo,alipay_no,buyer_message,pic_path,num_iid,num,price,cod_fee,cod_status,shipping_type,orders");

		Orders result = null;
		TradeGetResponse res = (TradeGetResponse) executeHttp(req, null);
		if (res != null && res.isSuccess()) {
			result = tradeToOrder(res.getTrade());
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#rebuildCategoryMenu()
	 */
	@Override
	public boolean rebuildCategoryMenu() {
		if (getCategoryMenu() == null) {
			categoryMenu = new Menu("Taobao.category", "taobao category mapping");
		}
		SellercatsListGetRequest req = new SellercatsListGetRequest();
		req.setNick(defaultNick);
		List<Category> categories = categoryService.getChild(0L);
		logger.debug("yd categories: {}", categories);
		for (Category category : categories) {
			if (categoryMenu.getOptions().get(category.getId().toString()) == null) {
				categoryMenu.addOption(category.getId().toString(), category.getName());
			}
		}
		SellercatsListGetResponse res = (SellercatsListGetResponse) executeHttp(req, null);
		if (res != null && res.isSuccess() && res.getSellerCats() != null) {
			logger.debug("taobao categories: {}", res.getSellerCats());
			for (SellerCat cat : res.getSellerCats()) {
				for (Option o : categoryMenu.getOptions().values()) {
					if (o.getName().equals(cat.getName())) {
						o.setMemo1(cat.getCid().toString());
					}
				}
			}
		}
		categoryMenu = menuService.save(categoryMenu);
		logger.debug("final categories: {}", categoryMenu.getOptions());
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#getParameters(java.lang.String, java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	@Override
	public Map<String, String> getParameters(String topAppkey, String topParameters, String topSession, String topSign) {

		Map<String, String> result = new HashMap<String, String>();

		int keySeq = 0;
		for (int i = 0; i < apiKey.length; i++) {
			if (apiKey[i].equals(topAppkey)) {
				keySeq = i;
			}
		}

		try {
			if (StringUtils.isBlank(topParameters)) {
				logger.debug("topParameters empty: topParameters=" + topParameters);
				return result;
			}
			// topParameters = URLDecoder.decode(topParameters, "utf-8");
			// topSign = URLDecoder.decode(topSign, "utf-8");

			StringBuffer sb = new StringBuffer();
			sb.append(topAppkey).append(topParameters).append(topSession).append(apiSecret[keySeq]);
			MessageDigest md = MessageDigest.getInstance("MD5");
			logger.debug("msg:" + sb.toString());
			byte[] b = md.digest(sb.toString().getBytes("UTF-8"));

			String localSign = new String(Base64.encodeBase64(b));
			if (!StringUtils.equals(topSign, localSign)) {
				logger.error("sign fail, localSign:" + localSign + ",topSign=" + topSign + ",topParameters=" + topParameters);
				return result;
			}

			String keyvalues = new String(Base64.decodeBase64(topParameters.getBytes()));

			if (StringUtils.isBlank(keyvalues)) {
				logger.debug("keyvalues empty: keyvalues=" + keyvalues + ",topParameters=" + topParameters);
				return result;
			}
			String[] keyvalueArray = keyvalues.split("&");
			for (String keyvalue : keyvalueArray) {
				String[] s = keyvalue.split("=");
				if (s == null || s.length != 2) {
					logger.debug("keyvalue error: s=" + s + ",topParameters=" + topParameters);
					return result;
				}
				result.put(s[0], s[1]);
			}
		} catch (Exception e) {
			logger.debug("get result exception,topParameters=" + topParameters, e);
			return result;
		}

		if (StringUtils.isNotBlank(result.get("ts"))) {
			Date now = DateUtil.getCurrentTime();
			long t = (new Date(Long.valueOf(result.get("ts"))).getTime() - now.getTime()) / 60000;
			if (20 > t && t > -20) {
				// valid ok
			} else {
				// valid fail
				logger.error("sign ok, time fail, current:{}, taobao time: {}, topParameters:{}",
						new Object[] { DateUtil.convertDateToString(now), result.get("ts"), topParameters });
				result.clear();
			}
		}
		logger.debug("result:" + result);
		return result;
	}

	public List<Map<String, Object>> getPublishProduct(String percentage, int minSpecInv) {
		StringBuffer sql = new StringBuffer();
		sql.append("select pm.product_id,pm.product_name,pm.modify_date product_modify,vs.modify_date shelf_modify,vs.SHELF_PRICE, pm.product_brand, pm.main_category_id, s.join_spec");
		sql.append(" from ec_product_main pm");
		sql.append(" left join ec_view_shelf vs on vs.PRODUCT_ID = pm.PRODUCT_ID and sysdate between vs.start_date and vs.end_date and vs.shelf_status='SHF500'");
		sql.append(" left join (select product_id, sum(available_inventory+cancel_inventory-sold_inventory) inventory from ec_specification_inventory group by product_id) si on si.product_id=pm.PRODUCT_ID");
		sql.append(" left join (select product_id, tab_to_string(CAST(COLLECT(DISTINCT SPECIFICATION_NAME) AS t_varchar2_tab)) join_spec from ec_specification where is_delete='N' group by product_id) s on s.product_id= pm.PRODUCT_ID");
		sql.append(" where vs.SHELF_PRICE is not null and pm.product_cost is not null and vs.SHELF_PRICE>0");
		sql.append(" and pm.is_delete='N' and pm.product_type='0' and pm.is_scm!='Y' and si.inventory > ");
		sql.append(minSpecInv);
		sql.append(" and (vs.SHELF_PRICE-pm.product_cost)/vs.SHELF_PRICE > ");
		sql.append(percentage);
		sql.append(" order by pm.product_id asc");
		List<Map<String, Object>> products = jdbcTemplate.queryForList(sql.toString());
		return products;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#buildFeedFull(java.util.Map)
	 */
	@Override
	public String buildFeedHead(String templateName, Map<String, String> operation) {
		Map objs = new HashMap();
		objs.put("optMap", operation);
		String result = templateService.format(templateName, objs);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#buildFeedCats()
	 */
	@Override
	public String buildFeedCats(Date feedModifyDate) {
		String result = null;
		boolean rebuild = false;
		Map objs = new HashMap();
		List<Category> categories = categoryService.getVisibleChild(0L);
		for (Category c : categories) {
			if (c.getModifyDate().after(feedModifyDate)) {
				logger.debug("build FeedCats, category:{}/{}, feed modify:{}", new Object[] { c.getId(), c.getModifyDate(),
						feedModifyDate });
				rebuild = true;
				break;
			}
		}
		if (rebuild) {
			objs.put("catList", categories);
			result = templateService.format("taobao/feed_cats", objs);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.taobao.service.TaobaoService#buildFeedItem(java.lang.String)
	 */
	@Override
	public String buildFeedItem(Map<String, Object> pid, Date feedModifyDate) {
		String result = null;

		Map objs = new HashMap(pid);

		String productName = objs.get("PRODUCT_NAME").toString();
		String desc = productService.readFromFile(objs.get("PRODUCT_ID").toString(), "description");
		if (StringUtils.isBlank(desc)) {
			desc = productService.readFromFile(objs.get("PRODUCT_ID").toString(), "details");
			if (StringUtils.isBlank(desc)) {
				desc = productService.readFromFile(objs.get("PRODUCT_ID").toString(), "brief");
			}
		}
		if (desc != null && desc.getBytes().length > 1000) {
			desc = StringUtils.removeHTML(desc, false);
		}
		if (StringUtils.isBlank(desc)) {
			desc = productName;
		}
		if (desc == null || desc.length() < 5) {
			desc = "＊＊＊＊＊＊";
		}
		if (desc.getBytes().length > 1000) {
			desc = new String(desc.getBytes(), 0, 1000);
		}
		objs.put("desc", desc);
		if (productName.getBytes().length > 60) {
			objs.put("PRODUCT_NAME", new String(productName.getBytes(), 0, 60));
		}

		Brand brand = null;
		if (objs.get("PRODUCT_BRAND") != null && StringUtils.isNotBlank(objs.get("PRODUCT_BRAND").toString())) {
			String brandId = objs.get("PRODUCT_BRAND").toString();
			if (cacheBrand != null) {
				try {
					brand = cacheBrand.get(brandId);
				} catch (CacheException e) {
					brand = productService.getBrandByBrandId(brandId);
					cacheBrand.put(brandId, brand);
				}
			} else {
				brand = productService.getBrandByBrandId(brandId);
			}
		}
		objs.put("brand", brand);

		Category category = null;
		String mainCategoryId = String.valueOf(objs.get("MAIN_CATEGORY_ID"));
		if (cacheCategory != null) {
			try {
				category = cacheCategory.get(mainCategoryId);
			} catch (CacheException e) {
				category = categoryService.get(Long.valueOf(mainCategoryId));
				cacheCategory.put(mainCategoryId, category);
			}
		} else {
			category = categoryService.get(Long.valueOf(mainCategoryId));
		}
		objs.put("category", category);

		result = templateService.format("taobao/feed_item", objs);
		return result;
	}

}
